#include "Hand.h"

namespace core
{

////////////////////////// Box implementation ////////////////////////////

Box::Box(Card* first_card_ptr, int box_size)
	: mFront(first_card_ptr)
	, mBack(mFront + box_size - 1)
	, mIter(mFront)
{
	mIter += count_ready_cards();
	mCardsRest = back() - curr() + 1;
}

uint8 Box::count_ready_cards() const
{
	int res = 0;
	Card* it = beg();
	while (!it->empty() && it <= back())
	{
		res++;
		it++;
	}
	return res;
}

void Box::clear()
{
	Card empty_card(0,0);
	mIter = back() + 1;
	while (--mIter != beg())
		*mIter = empty_card;
	mCardsRest = back() - beg() + 1;
}

int Box::insert(Card c)
{
	if (full())
		return -1;

	*mIter++ = c;
	return (--mCardsRest);
}

uint8 Box::power()
{
	uint8 res(0);
	for (;;)
	{
		//std::cout << "is_straight_flush()\n";
		res = is_straight_flush();
		if (res)
			break;

		//std::cout << "is_quads()\n";
		res = is_quads();
		if (res)
			break;

		//std::cout << "is_full_house()\n";
		res = is_full_house();
		if (res)
			break;

		//std::cout << "is_flush()\n";
		res = is_flush();
		if (res)
			break;

		//std::cout << "is_straight()\n";
		res = is_straight();
		if (res)
			break;

		//std::cout << "is_set()\n";
		res = is_set();
		if (res)
			break;

		//std::cout << "is_pairs()\n";
		res = is_pairs();
		if (res)
			break;

		res = max_rank();
		break;
	}

	return res;
}



std::ostream& operator<< (std::ostream &output, Box obj)
{
	if (obj.empty())
		output << "empty";
	else
	{
		Card* it = obj.beg();
		while (it != obj.curr())
			output << *it++ << ' ';
	}
	return output;
}

////////////////////////// Hand implementation ////////////////////////////

Hand::Hand()
	: mCardsCount(0)
	, mBox1(mCards, BOX1_NUM_OF_CARDS)
	, mBox2(mCards + BOX1_NUM_OF_CARDS, BOX2_NUM_OF_CARDS)
	, mBox3(mCards + BOX1_NUM_OF_CARDS + BOX2_NUM_OF_CARDS, BOX3_NUM_OF_CARDS)
{
	mBoxes[0] = &mBox1;
	mBoxes[1] = &mBox2;
	mBoxes[2] = &mBox3;
	for (int i = 0; i < NUM_OF_BOXES_IN_HAND; i++)
	{
		mBoxes[i]->clear();
		mPower[i] = 0;
	}
}

int Hand::insert(Card c, uint8 box_num)
{
	if (box_num - 1 > NUM_OF_BOXES_IN_HAND)
		return -1;
	if (mBoxes[box_num - 1]->insert(c) < 0)
		return -1;
	mCardsCount++;
	return (mCardsCount);
}

std::ostream& operator<< (std::ostream &output, Hand obj)
{
	for (uint8 box = 0; box < NUM_OF_BOXES_IN_HAND; box++)
		output << "Box " << box + 1 << ": " << *(obj.mBoxes[box]) << std::endl;
	return output;
}

bool Hand::valid() const
{
	//return (mBox1.power() <= mBox2.power()) && (mBox2.power() <= mBox3.power());
	if (*mPower == 0)
		return false;
	return (mPower[0] <= mPower[1]) && (mPower[1] <= mPower[2]);
}

void Hand::powers_count()
{
	for (uint8 box_num = 0; box_num < NUM_OF_BOXES_IN_HAND; box_num++)
		mPower[box_num] = mBoxes[box_num]->power();
	if (!valid())
		for (uint8 box_num = 0; box_num < NUM_OF_BOXES_IN_HAND; box_num++)
			mPower[box_num] = 0;
}

///////////////////// for hand's power searh ////////////////////

uint8 Box::max_rank()
{
	uint8 max_rnk = beg()->rank();
	Card* c_it = beg();
	while (c_it != curr())
	{
		if (c_it->rank() > max_rnk)
			max_rnk = c_it->rank();
		c_it++;
	}
	return max_rnk;
}

uint8 Box::min_rank()
{
	uint8 min_rnk = beg()->rank();
	Card* c_it = beg();
	while (c_it != curr())
	{
		if (c_it->rank() < min_rnk)
			min_rnk = c_it->rank();
		c_it++;
	}
	return min_rnk;
}

uint8 Box::is_straight_flush()
{
	uint8 res = is_flush();
	if (res)
		res += (is_straight())? 50 : 0;
	return res;
}

uint8 Box::count_rank(uint8 rnk)
{
	uint8 counter(0);

	Card* c_it = beg();
	while (c_it != curr())
		if (c_it++->rank() == rnk) counter++;

	return counter;
}

uint8 Box::is_straight()
{
	uint8 res = 0;
	uint8 max_rnk = max_rank();
	uint8 min_rnk = min_rank();

	if (max_rnk - min_rnk != 4)
		return 0;

	uint8 mid1(min_rnk + 1);
	uint8 mid2(min_rnk + 2);
	uint8 mid3(min_rnk + 3);

	bool m1(false), m2(false), m3(false);

	Card* c_it = beg() - 1;
	while (++c_it != curr())
	{
		if (c_it->rank() == mid1)
		{
			m1 = true;
			continue;
		}
		if (c_it->rank() == mid2)
		{
			m2 = true;
			continue;
		}
		if (c_it->rank() == mid3)
		{
			m3 = true;
			continue;
		}
	}

	if (m1 && m2 && m3)
		res = 70 + max_rnk;
	else
	{
		if (max_rnk == ACE);
		// TODO: check wheel (A2345)
	}


	return res;
}

uint8 Box::is_flush()
{
	uint8 res = 0;
	uint8 suit = beg()->suit();
	Card* c_it = beg();

	while (++c_it != curr())
		if (c_it->suit() != suit) break;

	if (c_it - beg() >= STRONG_COMBINATIONS_MIN_CARDS)
		res = 90 + max_rank();

	return res;
}

uint8 Box::is_quads()
{
	uint8 res = 0;
	uint8 value = beg()->rank();
	Card* c_it = beg();

	uint8 count_bad(0);
	while (++c_it != curr())
		if (c_it->rank() != value) count_bad++;
	if (count_bad < 2)
		return 130 + value;

	c_it = beg() + 1;
	if (c_it->rank() != value)
	{
		while (++c_it != curr())
			if (c_it->rank() != value) count_bad++;
		if (count_bad < 2)
			return 130 + value;
	}
	return res;
}

uint8 Box::is_set()
{
	Card* c_it = beg();
	for (int i = 0; i < 3; i++)
	{
		c_it = beg() + i;
		uint8 val = c_it->rank();

		uint8 counter(1);
		while (++c_it != curr())
			if (c_it->rank() == val) counter++;
		if (counter == 3)
			return 60 + val;
	}
	return 0;
}

uint8 Box::is_pairs()
{
	uint8 counter(0);
	uint8 val(0);
	for (uint8 rnk = 2; rnk <= ACE; rnk++)
		if (count_rank(rnk) == 2)
		{
			counter++;
			val = rnk;
		}
	switch (counter)
	{
	case 1:
		return val + 20;
	case 2:
		return val + 40;
	default:
		return 0;
	}
	return 0;
}

uint8 Box::is_full_house()
{
	if (is_pairs())
		if (is_set())
			return is_set() + 50;
	return 0;
}

} // namespace core